#include "Camera.h"
#include "RenderDefines.h"

// Controls how wide we turn when looking.
static const int LOOK_MAGNITUDE = 6;

Camera::Camera(void)
{
	// Projection.
	aspect = 4.0f / 3.0f;
	fov = 45.0f;
	znear = 0.1f;
	zfar = 100.0f;
	projection = glm::perspective(fov,aspect,znear,zfar);

	// View.
	ground_forward = FORWARD;			// The forward that we translate to.
	look_forward = FORWARD;				// The forward that we look to.
	mRight = RIGHT;
	_transform._position = glm::vec4(0,0,10,0);
	view = glm::lookAt(glm::vec3(_transform._position), look_forward, UP);

	// Final.
	final = projection * view;
}


Camera::~Camera(void)
{
}


// Get a reference to the camera matrix.
glm::mat4* Camera::getCameraMatrix( ){
	return &final;
}

void Camera::AddTranslation( glm::vec3 translation ){
	_transform._translation += translation;
}
void Camera::AddRotation( glm::vec3 rotation ){
	_transform._rotation += rotation;
}


void Camera::setPosition( glm::vec4 pos ){
	_transform._position=pos;
}

void Camera::getPosition( glm::vec4 &pos ){
	pos = _transform._position;
}

void Camera::Find_look_forward(){
	// Get the mouse_xy position.
	glm::ivec2 m(0);
	glfwGetMousePos(&m.x,&m.y);

	// Find our window size.
	int w = RENDER_WIDTH;
	int h = RENDER_HEIGHT;

	// Create our look_forward screen space.
	glm::ivec2 to_lookspace( w/2, h/2 );

	// Transform our mouse_xy to this space.
	m -= to_lookspace;

	// Normalize our mouse_xy to (-1_1).
	glm::vec2 m_normalized( 
		static_cast<float>(m.x) / static_cast<float>(to_lookspace.x), 
		static_cast<float>(m.y) / static_cast<float>(to_lookspace.y) );
	
	// Cap the normal values.
	m_normalized.x = THECLAMP( m_normalized.x, -1.0f, 1.0f );
	m_normalized.y = THECLAMP( m_normalized.y, -1.0f, 1.0f );

	//printf("lookspace mouse :(%f,%f)\n", m_normalized.x, m_normalized.y );

	// Scale our rotation.
	float r_scale = 30.0f;

	// Rotate our look_forward vector.
	look_forward = FORWARD;									// Make us start by looking to default forward.
	look_forward *= glm::vec3( LOOK_MAGNITUDE );			// Give our look some  magnitude so we turn wider.
	look_forward = glm::rotateX( look_forward, m_normalized.y * r_scale);
	look_forward = glm::rotateY( look_forward, m_normalized.x * r_scale);

	// Adjust our right.
	mRight = glm::rotateX( mRight, m_normalized.y * r_scale );
	mRight = glm::rotateY( mRight, m_normalized.x * r_scale );
}


void Camera::Find_ground_forward(){
	// We want to rotate our ground_forward.xz to match the look_forward.xy.
	// Lets just project it.
	ground_forward = glm::vec3( look_forward.x, ground_forward.y, look_forward.z);
}

void Camera::ComputeMatrix(){
	// Move us towards the ground forward.
		// We use the translation as a magnitude in the direction of our ground_forward.
	/*glm::vec4 movement = glm::vec4(
		_transform._position.x * look_forward.x,
		_transform._position.y,
		_transform._position.z * look_forward.z,
		1
		);*/

	// Compute the view.
	//printf("look: (%f, %f, %f)\n",look_forward.x, look_forward.y, look_forward.z);
	view = glm::lookAt( glm::vec3(_transform._position), look_forward, UP );
}

void Camera::move_1( )
{
	// Moves the camera by dx world units to the left or right; dy
	// world units upwards or downwards; and dz world units forwards
	// or backwards.

	glm::vec3 eye = glm::vec3( _transform._position );
	glm::vec3 forwards = look_forward;


	// Calculate the forwards direction. Can't just use the camera's local
	// z axis as doing so will cause the camera to move more slowly as the
	// camera's view approaches 90 degrees straight up and down.

	forwards = glm::cross( UP, mRight );
	forwards = glm::normalize( forwards );

	eye += mRight * _transform._translation.x;
	eye += UP * _transform._translation.y;
	eye += forwards * _transform._translation.z;
	_transform._translation = glm::vec3(0);

	_transform._position = glm::vec4( eye.x, eye.y, eye.z, 1 );
}

void Camera::Update( GLdouble time ){
	Find_look_forward();
	move_1();

	ComputeMatrix();

	final = projection * view;
}